//------------------------------------------------------------------------
template<typename T> u8 
csStringBase<T>::ms_emptyStringRep[sizeof(s32)+sizeof(s32)+sizeof(s32)+sizeof(T)];

//------------------------------------------------------------------------
template<typename T> csINLINE T*
csStringBase<T>::Rep::getData()
{
	return reinterpret_cast<T*>(this+1);
}

//------------------------------------------------------------------------
template<typename T> csINLINE const T*
csStringBase<T>::Rep::getData() const
{
	return reinterpret_cast<const T*>(this+1);
}

//------------------------------------------------------------------------
template<typename T> csINLINE T*
csStringBase<T>::Rep::refCopy()
{
	++m_references;
	return getData();
}

//------------------------------------------------------------------------
template<typename T> csINLINE s32
csStringBase<T>::Rep::getSize() const
{
	return sizeof(Rep) + sizeof(T)*(m_capacity + 1);
}

//------------------------------------------------------------------------
template<typename T> csINLINE void
csStringBase<T>::Rep::unRef()
{
	if(--m_references<0)
	{
		freeMemory();
	}
}

//------------------------------------------------------------------------
template<typename T> void
csStringBase<T>::Rep::freeMemory()
{
	free(this);
}

//------------------------------------------------------------------------
template<typename T> typename csStringBase<T>::Rep* 
csStringBase<T>::Rep::clone() const
{
	Rep* r = reinterpret_cast<Rep*>(malloc(getSize()));
	r->m_length		= m_length;
	r->m_capacity	= m_capacity;
	r->m_references	= 0;
	csMemCpy(r->getData(),getData(),m_length+1);
	return r;
}

//------------------------------------------------------------------------
template<typename T> typename csStringBase<T>::Rep*
csStringBase<T>::Rep::Create(s32 len)
{
	u32 scapacity = len;
	if(scapacity < csMININUM_STRING_CAPACITY)
	{
		scapacity = csMININUM_STRING_CAPACITY;
	}
	Rep* r = reinterpret_cast<Rep*>(malloc(sizeof(Rep)+sizeof(T)*(scapacity+1)));
	r->m_length		= len;
	r->m_capacity	= scapacity;
	r->m_references	= 0;
	return r;
}

//------------------------------------------------------------------------
template<typename T> csINLINE
csStringBase<T>::csStringBase(typename csStringBase<T>::Rep* r)
:m_string(r->getData())
{

}

//------------------------------------------------------------------------
template<typename T> csINLINE typename csStringBase<T>::Rep&
csStringBase<T>::_getRep() const
{
	return *(reinterpret_cast<Rep*>(m_string)-1);
}

//------------------------------------------------------------------------
template<typename T> csINLINE typename csStringBase<T>::Rep&
csStringBase<T>::_GetEmptyStringRep()
{
	return *reinterpret_cast<Rep*>(ms_emptyStringRep);
}

//------------------------------------------------------------------------
template<typename T> csINLINE
csStringBase<T>::csStringBase()
{
	m_string = _GetEmptyStringRep().refCopy();
}

//------------------------------------------------------------------------
template<typename T> csINLINE
csStringBase<T>::csStringBase(const T* s)
{
	if(csNULL!=s)
	{
		s32 slen = csStrLen(s);
		m_string = Rep::Create(slen)->getData();
		csMemCpy(m_string,s,slen+1);
	}
	else
	{
		m_string = _GetEmptyStringRep().refCopy();
	}
}

//------------------------------------------------------------------------
template<typename T> csINLINE
csStringBase<T>::csStringBase(T ch, s32 len)
{
	csASSERT(len>=0);
	m_string = Rep::Create(len)->getData();
	csMemSet(m_string,ch,len);
	m_string[len] = 0;
}

//------------------------------------------------------------------------
template<typename T> csINLINE
csStringBase<T>::csStringBase(const T* buf, s32 len)
{
	csASSERT(buf);
	if(csINVALID_INDEX==len)
	{
		len = csStrLen(buf);
	}
	else
	{
		s32 slen = csStrLen(buf);
		if(len>slen)
		{
			len = slen;
		}
	}
	csASSERT(len>=0);
	m_string = Rep::Create(len)->getData();
	csMemCpy(m_string,buf,len);
	m_string[len] = 0;
}

//------------------------------------------------------------------------
template<typename T> csINLINE
csStringBase<T>::csStringBase(const csStringBase<T>& other)
{
	m_string = other._getRep().refCopy();
}

//------------------------------------------------------------------------
template<typename T> csINLINE
csStringBase<T>::~csStringBase()
{
	_getRep().unRef();
}

//------------------------------------------------------------------------
template<typename T> csINLINE csStringBase<T>&
csStringBase<T>::operator = (const csStringBase<T>& s)
{
	T* newStr = s._getRep().refCopy();
	_getRep().unRef();
	m_string = newStr;
	return (*this);
}

//------------------------------------------------------------------------
template<typename T> csINLINE csStringBase<T>&
csStringBase<T>::operator = (const T* s)
{
	if(csNULL!=s&&0!=*s)
	{
		s32 slen = csStrLen(s);
		if(_getRep().m_capacity<slen||_getRep().m_references>0)
		{
			_getRep().unRef();
			m_string = Rep::Create(slen)->getData();
		}
		csMemCpy(m_string,s,slen+1);
		_getRep().m_length = slen;
	}
	else
	{
		_getRep().unRef();
		m_string = _GetEmptyStringRep().refCopy();
	}
	return (*this);
}

//------------------------------------------------------------------------
template<typename T> csINLINE T
csStringBase<T>::operator [](s32 i) const
{
	return m_string[i];
}

//------------------------------------------------------------------------
template<typename T> csINLINE s32
csStringBase<T>::length() const
{
	return _getRep().m_length;
}

//------------------------------------------------------------------------
template<typename T> csINLINE bool
csStringBase<T>::empty() const
{
	return 0==length();
}

//------------------------------------------------------------------------
template<typename T> void
csStringBase<T>::printf(const T* format, ...)
{
	va_list argptr;
	va_start(argptr, format);
	printfV(format, argptr);
	va_end(argptr);
}

//------------------------------------------------------------------------
template<typename T> void
csStringBase<T>::printfV(const T* format, va_list args)
{
	if(_getRep().m_references>0||length()<csMININUM_STRING_CAPACITY)
	{
		_getRep().unRef();
		m_string = Rep::Create(255)->getData();
	}

	while(1)
	{
		s32 size	= _getRep().m_capacity;
		s32 nchars	= csVsnprintf(m_string,size,format,args);
		if(nchars>=0&&nchars<size)
		{
			_getRep().m_length = nchars;
			break;
		}
		else if(nchars<0)
		{
			_getRep().unRef();
			m_string	= Rep::Create(size*2>255?size*2:255)->getData();
		}
		else
		{
			_getRep().unRef();
			m_string	= Rep::Create(nchars)->getData();
		}
	}
}

//------------------------------------------------------------------------
template<typename T> csINLINE const T*
csStringBase<T>::c_str() const
{
	return m_string;
}

//------------------------------------------------------------------------
template<typename T> void
csStringBase<T>::reserve(s32 n)
{
	csASSERT(n>=0);
	if(_getRep().m_capacity>=n&&_getRep().m_references<=0)
	{
		return;
	}
	s32 len = length();
	Rep* rep = Rep::Create(n);
	csMemCpy(rep->getData(),c_str(),len);
	rep->getData()[len] = 0;
	rep->m_length = len;
	_getRep().unRef();
	m_string = rep->getData();
}

//------------------------------------------------------------------------
template<typename T> csStringBase<T>
csStringBase<T>::subString(s32 index, s32 num) const
{
	csASSERT(index>=0&&index<length());
	csASSERT(num==csINVALID_INDEX||num>0);
	if(csINVALID_INDEX==num)
	{
		num = length() - index;
	}
	if(num+index>length())
	{
		num = length() - index;
	}
	Rep* r	= Rep::Create(num);
	T* p	= r->getData();
	csMemCpy(p,m_string+index,num);
	p[num] = 0;
	return csStringBase(r);
}

//------------------------------------------------------------------------
template<typename T> csStringBase<T>&
csStringBase<T>::remove(s32 idx)
{
	return remove(idx,1);
}

//------------------------------------------------------------------------
template<typename T> csStringBase<T>&
csStringBase<T>::remove(s32 idx, s32 len)
{
	csASSERT(idx>=0&&idx <= length());
	s32 strLen = length() - idx;
	len = strLen < len ? strLen : len;
	csStringBase<T> strTmp(c_str(), idx);
	strTmp+=(c_str()+idx+len);
	swap(strTmp);
	return *this;
}

//------------------------------------------------------------------------
template<typename T> csStringBase<T>&
csStringBase<T>::setChar(s32 idx, T ch)
{
	csASSERT(idx>=0&&idx<length());
	if(c_str()[idx]==ch)
	{
		return *this;
	}
	if(_getRep().m_references>0)
	{
		Rep* rep = _getRep().clone();
		_getRep().unRef();
		m_string = rep->getData();
	}
	m_string[idx] = ch;
	return *this;
}

//------------------------------------------------------------------------
template<typename T> csINLINE void
csStringBase<T>::swap(csStringBase<T>& other)
{
	csSwap<T*>(m_string,other.m_string);
}

//------------------------------------------------------------------------
template<typename T> csINLINE s32
csStringBase<T>::find(const csStringBase<T>& str, s32 nStart) const
{
	// deal with the special case of empty string first
	const s32 nLen = length();
	const s32 nLenOther = str.length();

	if ( !nLenOther )
	{
		// empty string is a substring of anything
		return 0;
	}

	if ( !nLen )
	{
		// the other string is non empty so can't be our substring
		return csINVALID_INDEX;
	}
	csASSERT( nStart <= nLen );
	const T* const other = str.c_str();

	// anchor
	const T* p = (const T*)csMemChr(c_str() + nStart,
		*other,
		nLen - nStart);

	if ( !p )
		return csINVALID_INDEX;

	while ( p - c_str() + nLenOther <= nLen && csMemCmp(p, other, nLenOther) )
	{
		p++;

		// anchor again
		p = (const T*)csMemChr(p, *other, nLen - (p - c_str()));

		if ( !p )
			return csINVALID_INDEX;
	}

	return p - c_str() + nLenOther <= nLen ? p - c_str() : csINVALID_INDEX;
}

//------------------------------------------------------------------------
template<typename T> csINLINE s32
csStringBase<T>::find(const T* sz, s32 nStart, s32 n) const
{
	return find(csStringBase<T>(sz, n), nStart);
}

//------------------------------------------------------------------------
template<typename T> csINLINE s32
csStringBase<T>::find(T ch, s32 nStart) const
{
	csASSERT(csINVALID_INDEX==nStart||nStart>=0);
	csASSERT( nStart <= length() );
	nStart += 1;
	const T *p = (const T*)csMemChr(c_str() + nStart, ch, length() - nStart);
	return p == csNULL ? csINVALID_INDEX : p - c_str();
}

//------------------------------------------------------------------------
template<typename T> csINLINE s32
csStringBase<T>::rfind(const csStringBase<T>& str, s32 nStart) const
{
	csASSERT( nStart == csINVALID_INDEX || nStart <= length() );

	if ( length() >= str.length() )
	{
		// avoids a corner case later
		if ( length() == 0 && str.length() == 0 )
			return 0;

		// "top" is the point where search starts from
		s32 top = length() - str.length();

		if ( nStart == csINVALID_INDEX )
			nStart = length() - 1;
		if ( nStart < top )
			top = nStart;

		const T *cursor = c_str() + top;
		do
		{
			if ( csMemCmp(cursor, str.c_str(),
				str.length()) == 0 )
			{
				return cursor - c_str();
			}
		} while ( cursor-- > c_str() );
	}

	return csINVALID_INDEX;
}

//------------------------------------------------------------------------
template<typename T> csINLINE s32
csStringBase<T>::rfind(const T* sz, s32 nStart, s32 n) const
{
	return rfind(csStringBase<T>(sz, n), nStart);
}

//------------------------------------------------------------------------
template<typename T> csINLINE s32
csStringBase<T>::rfind(T ch, s32 nStart) const
{
	if ( nStart == csINVALID_INDEX )
	{
		nStart = length();
	}
	else
	{
		csASSERT(nStart <= length()&&nStart>=0);
	}

	const T *actual;
	for ( actual = c_str() + ( nStart == csINVALID_INDEX ? length() : nStart + 1 );
		actual > c_str(); --actual )
	{
		if ( *(actual - 1) == ch )
			return (actual - 1) - c_str();
	}

	return csINVALID_INDEX;
}

//------------------------------------------------------------------------
template<typename T> csINLINE csStringBase<T>&
csStringBase<T>::insert(s32 nPos, const csStringBase<T> &str)
{
	return insert(nPos,str,0,str.length());
}
//------------------------------------------------------------------------
template<typename T> csINLINE csStringBase<T>&
csStringBase<T>::insert(s32 nPos, const csStringBase<T>& str, s32 nStart, s32 n)
{
	csASSERT(nStart>=0);
	csASSERT(nStart+n<=str.length());
	reserve(length()+n);
	memmove(m_string+nPos+n,m_string+nPos,(length()-nPos)*sizeof(T));
	csMemCpy(m_string+nPos,str.m_string+nStart,n);
	_getRep().m_length = length()+n;
	m_string[length()] = 0;
	return *this;
}

//------------------------------------------------------------------------
template<typename T> csINLINE csStringBase<T>&
csStringBase<T>::insert(s32 nPos, const T* sz)
{
	csASSERT(sz);
	s32 n = csStrLen(sz);
	return insert(nPos,sz,n);
}

//------------------------------------------------------------------------
template<typename T> csINLINE csStringBase<T>&
csStringBase<T>::insert(s32 nPos, const T* sz, s32 n)
{
	csASSERT(sz);
	csASSERT(csStrLen(sz)>=n);
	reserve(length()+n);
	memmove(m_string+nPos+n,m_string+nPos,(length()-nPos)*sizeof(T));
	csMemCpy(m_string+nPos,sz,n);
	_getRep().m_length = length()+n;
	m_string[length()] = 0;
	return *this;
}

//------------------------------------------------------------------------
template<typename T> csINLINE csStringBase<T>&
csStringBase<T>::insert(s32 nPos, s32 n, T ch)
{
	csASSERT(n>=0);
	reserve(length()+n);
	memmove(m_string+nPos+n,m_string+nPos,(length()-nPos)*sizeof(T));
	csMemSet(m_string+nPos,ch,n);
	_getRep().m_length = length()+n;
	m_string[length()] = 0;
	return *this;
}

//------------------------------------------------------------------------
template<typename T> csINLINE csStringBase<T>&
csStringBase<T>::replace(s32 idx, s32 n, csStringBase<T>& str)
{
	return replace(idx,n,str.c_str(),0,str.length());
}

//------------------------------------------------------------------------
template<typename T> csINLINE csStringBase<T>&
csStringBase<T>::replace(s32 idx, s32 n, csStringBase<T>& str, s32 sidx, s32 sn)
{
	return replace(idx,n,str.c_str(),sidx,sn);
}

//------------------------------------------------------------------------
template<typename T> csINLINE csStringBase<T>&
csStringBase<T>::replace(s32 idx, s32 n, const T* str)
{
	csASSERT(str);
	s32 len = csStrLen(str);
	csASSERT(idx>=0&&idx<length());
	csASSERT(n>=0&&n+idx<=length());
	csStringBase<T> tmp;
	tmp.reserve(length()-n+len);
	tmp.append(c_str(),0,idx);
	tmp.append(str,0,len);
	tmp.append(c_str(),idx+n,length()-idx-n);
	swap(tmp);
	return *this;
}

//------------------------------------------------------------------------
template<typename T> csINLINE csStringBase<T>&
csStringBase<T>::replace(s32 idx, s32 n, const T* str, s32 sidx, s32 sn)
{
	csASSERT(str);
	s32 len = csStrLen(str);
	csASSERT(idx>=0&&idx<length());
	csASSERT(n>=0&&n+idx<=length());
	csASSERT(sidx>=0&&sidx<len);
	csASSERT(sn>=0&&sidx+sn<=length());
	csStringBase<T> tmp;
	tmp.reserve(length()-n+sn);
	tmp.append(c_str(),0,idx);
	tmp.append(str,sidx,sn);
	tmp.append(c_str(),idx+n,length()-idx-n);
	swap(tmp);
	return *this;
}

//------------------------------------------------------------------------
template<typename T> csINLINE csStringBase<T>&
csStringBase<T>::replace(s32 idx, s32 n, T ch, s32 cn)
{
	csASSERT(cn>=0);
	csASSERT(idx>=0&&idx<length());
	csASSERT(n>=0&&idx+n<=length());
	csStringBase<T> tmp;
	tmp.reserve(length()-n+cn);
	tmp.append(c_str(),0,idx);
	tmp.append(ch,cn);
	tmp.append(c_str(),idx+n,length()-idx-n);
	swap(tmp);
	return *this;
}

//------------------------------------------------------------------------
template<typename T> csINLINE csStringBase<T>&
csStringBase<T>::append(const csStringBase<T>& str)
{
	reserve(length()+str.length());
	csMemCpy(m_string+length(),str.c_str(),str.length()+1);
	_getRep().m_length = length()+str.length();
	return *this;
}

//------------------------------------------------------------------------
template<typename T> csINLINE csStringBase<T>&
csStringBase<T>::append(const csStringBase<T>& str, s32 idx, s32 n)
{
	return append(str.c_str(),idx,n);
}

//------------------------------------------------------------------------
template<typename T> csINLINE csStringBase<T>&
csStringBase<T>::append(const T *str)
{
	csASSERT(str);
	s32 len = csStrLen(str);
	reserve(length()+len);
	csMemCpy(m_string+length(),str,len+1);
	_getRep().m_length = length() + len;
	return *this;
}

//------------------------------------------------------------------------
template<typename T> csINLINE csStringBase<T>&
csStringBase<T>::append(const T* str, s32 idx, s32 n)
{
	if(0==n)
	{
		return *this;
	}
	csASSERT(str);
	s32 len = csStrLen(str);
	csASSERT(idx>=0&&idx<len);
	csASSERT(n>=0);
	if(csINVALID_INDEX==n||n+idx>len)
	{
		n = len - idx;
	}
	reserve(length()+n);
	csMemCpy(m_string+length(),str+idx,n);
	_getRep().m_length = length() + n;
	m_string[length()] = 0;
	return *this;
}

//------------------------------------------------------------------------
template<typename T> csINLINE csStringBase<T>&
csStringBase<T>::append(T ch, s32 n)
{
	csASSERT(n>=0);
	reserve(length()+n);
	csMemSet(m_string+length(),ch,n);
	_getRep().m_length = length() + n;
	m_string[length()] = 0;
	return *this;
}

//------------------------------------------------------------------------
template<typename T> csINLINE s32
csStringBase<T>::compare(const csStringBase<T>& other) const
{
	return csStrCmp(c_str(),other.c_str());
}

//------------------------------------------------------------------------
template<typename T> csINLINE s32
csStringBase<T>::compare(const T* other) const
{
	return csStrCmp(c_str(),other);
}

//------------------------------------------------------------------------
template<typename T> csINLINE s32
csStringBase<T>::compareIgnoreCase(const csStringBase<T> &other) const
{
	return csStrCaseCmp(c_str(),other.c_str());
}

//------------------------------------------------------------------------
template<typename T> csINLINE s32
csStringBase<T>::compareIgnoreCase(const T *other) const
{
	return csStrCaseCmp(c_str(),other);
}

//------------------------------------------------------------------------
template<typename T> bool
csStringBase<T>::beginsWith(const csStringBase<T>& s) const
{
	return beginsWith(s.c_str());
}

//------------------------------------------------------------------------
template<typename T> bool
csStringBase<T>::beginsWith(const T* s) const
{
	for(s32 i=0;s[i]!=0;++i)
	{
		if(i>=length()||c_str()[i]!=s[i])
		{
			return false;
		}
	}
	return true;
}

//------------------------------------------------------------------------
template<typename T> bool
csStringBase<T>::endsWith(const csStringBase<T>& s) const
{
	if(length()<s.length())
	{
		return false;
	}
	s32 offset = length() - s.length();
	for(s32 i=0;i<s.length();++i)
	{
		if(c_str()[i+offset]!=s[i])
		{
			return false;
		}
	}
	return true;
}

//------------------------------------------------------------------------
template<typename T> bool
csStringBase<T>::endsWith(const T* s) const
{
	csASSERT(s);
	s32 offset = length() - csStrLen(s);
	if(offset<0)
	{
		return false;
	}
	for(s32 i=0;s[i]!=0;++i)
	{
		if(c_str()[i+offset]!=s[i])
		{
			return false;
		}
	}
	return true;
}

//------------------------------------------------------------------------
template<typename T> csStringBase<T>&
csStringBase<T>::asLower()
{
	if(_getRep().m_capacity>0)
	{
		Rep* rep = _getRep().clone();
		_getRep().unRef();
		m_string = rep->getData();
	}
	for(s32 i=0;i<length();++i)
	{
		m_string[i] = csToLower(m_string[i]);
	}
	return *this;
}

//------------------------------------------------------------------------
template<typename T> csStringBase<T>&
csStringBase<T>::asUpper()
{
	if(_getRep().m_capacity>0)
	{
		Rep* rep = _getRep().clone();
		_getRep().unRef();
		m_string = rep->getData();
	}
	for(s32 i=0;i<length();++i)
	{
		m_string[i] = csToUpper(m_string[i]);
	}
	return *this;
}

//------------------------------------------------------------------------
template<typename T> csINLINE bool
csStringBase<T>::operator==(const csStringBase<T>& rhs) const
{
	if(length()!=rhs.length())
	{
		return false;
	}
	return 0==compare(rhs);
}

//------------------------------------------------------------------------
template<typename T> csINLINE bool
csStringBase<T>::operator==(const T* rhs) const
{
	return 0==compare(rhs);
}

//------------------------------------------------------------------------
template<typename T> csINLINE bool
csStringBase<T>::operator!=(const csStringBase<T>& rhs) const
{
	if(length()!=rhs.length())
	{
		return true;
	}
	return 0!=compare(rhs);
}

//------------------------------------------------------------------------
template<typename T> csINLINE bool
csStringBase<T>::operator!=(const T* rhs) const
{
	return 0!=compare(rhs);
}

//------------------------------------------------------------------------
template<typename T> csINLINE bool
csStringBase<T>::operator<(const csStringBase<T>& rhs) const
{
	return compare(rhs)<0;
}

//------------------------------------------------------------------------
template<typename T> csINLINE bool
csStringBase<T>::operator<=(const csStringBase<T>& rhs) const
{
	return compare(rhs)<=0;
}

//------------------------------------------------------------------------
template<typename T> csINLINE bool
csStringBase<T>::operator> (const csStringBase<T>& rhs) const
{
	return compare(rhs)>0;
}

//------------------------------------------------------------------------
template<typename T> csINLINE bool
csStringBase<T>::operator>=(const csStringBase<T>& rhs) const
{
	return compare(rhs)>=0;
}

//------------------------------------------------------------------------
template<typename T> csStringBase<T>
csStringBase<T>::operator +(const csStringBase<T>& rhs) const
{
	s32 len = length() + rhs.length();
	Rep* r	= Rep::Create(len);
	T* p	= r->getData();
	csMemCpy(p,c_str(),length());
	csMemCpy(p+length(),rhs.c_str(),rhs.length()+1);
	return csStringBase<T>(r);
}

//------------------------------------------------------------------------
template<typename T> csStringBase<T>
csStringBase<T>::operator +(const T* rhs) const
{
	s32 rLen = csStrLen(rhs);
	s32 len	 = length() + rLen;
	Rep* r	 = Rep::Create(len);
	T* p	 = r->getData();
	csMemCpy(p,c_str(),length());
	csMemCpy(p+length(),rhs,rLen+1);
	return csStringBase<T>(r);
}

//------------------------------------------------------------------------
template<typename T> csStringBase<T>
csStringBase<T>::operator +(T ch) const
{
	Rep* r	= Rep::Create(length()+1);
	T* p	= r->getData();
	csMemCpy(p,c_str(),length());
	p[length()] = ch;
	p[length()+1] = 0;
	return csStringBase<T>(r);
}

//------------------------------------------------------------------------
template<typename T> csStringBase<T>&
csStringBase<T>::operator+=(const csStringBase<T>& str)
{
	return append(str);
}

//------------------------------------------------------------------------
template<typename T> csStringBase<T>&
csStringBase<T>::operator+=(const T* str)
{
	return append(str);
}

//------------------------------------------------------------------------
template<typename T> csStringBase<T>&
csStringBase<T>::operator+=(T ch)
{
	return append(ch,1);
}

//------------------------------------------------------------------------
template<typename T> csStringBase<T>
csStringBase<T>::Format(const T* format, ...)
{
	csStringBase<T> str;
	va_list argptr;
	va_start(argptr, format);
	str.printfV(format, argptr);
	va_end(argptr);
	return str;
}
